package com.netx.generics.translation;
import com.netx.generics.basic.Checker;
import com.netx.generics.basic.IntegrityException;
import com.netx.generics.util.Text;

import java.io.Reader;
import java.io.IOException;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;



public abstract class GenericScanner extends FirstStep<Reader> {

	protected final Recognizer[] recognizers;
	protected final CommentAdvancer[] advancers;
	private final boolean _skipWhitespace;
	
	public GenericScanner(Reader reader, Recognizer[] recognizers, boolean skipWhitespace, CommentAdvancer[] advancers) {
		super(reader);
		Checker.checkEmpty(recognizers, "recognizers");
		Checker.checkNullElements(recognizers, "recognizers");
		this.recognizers = recognizers;
		if(advancers == null) {
			this.advancers = new CommentAdvancer[0];
		}
		else {
			Checker.checkNullElements(advancers, "advancers");
			this.advancers = advancers;
		}
		_skipWhitespace = skipWhitespace;
	}

	public GenericScanner(Reader reader, Recognizer[] recognizers, boolean skipWhitespace) {
		this(reader, recognizers, skipWhitespace, null);
	}

	public Object performWork(Results r) {
		Text text = null;
		try {
			text = new Text(getInitialObject());
		}
		catch(IOException io) {
			throw new IntegrityException(io);
		}
		Iterator<String> itLines = text.getLines().iterator();
		List<Token> tokens = new ArrayList<Token>();
		CommentAdvancer multilineComment = null;
		Position commentPosition = null;
		main:
		for(int lineNum=0; itLines.hasNext(); lineNum++) {
			String line = itLines.next();
			if(_skipWhitespace) {
				line = line.trim();
			}
			// Eliminate comment portions:
			advancers:
			while(true) {
				// Check whether we're passing a multiline comment:
				if(multilineComment != null) {
					line = multilineComment.checkLine(line);
					if(line == null) {
						// No end of multiline comment found; go to next line:
						continue main;
					}
					else {
						multilineComment = null;
						// Let the cycle go on, to check for additional
						// comments in this line.
					}
				}
				// Run comment advancers:
				//System.out.println("running advancers");
				for(CommentAdvancer advancer : advancers) {
					String checkedLine = advancer.checkLine(line);
					if(checkedLine != line) {
						line = checkedLine;
						if(advancer.isInsideComment()) {
							multilineComment = advancer;
							commentPosition = new Position(lineNum+1, multilineComment.getCommentIndex());
						}
						continue advancers;
					}
				}
				// No more comments found, ok to recognize Tokens:
				break advancers;
			}
			// Comment passed, recognize Tokens:
			_recognizeTokens(r, line, lineNum, tokens);
		}
		if(multilineComment != null) {
			r.addError("unterminated multiline comment", commentPosition);
		}
		return tokens;
	}
	
	private void _recognizeTokens(Results r, String line, int lineNum, List<Token> tokens) {
		int currPos = 0;
		run_recognizers:
		while(currPos<line.length()) {
			// Skip whitespace if required:
			if(_skipWhitespace) {
				while(currPos<line.length() && Character.isWhitespace(line.charAt(currPos))) {
					currPos++;
				}
			}
			if(currPos == line.length()) {
				return;
			}
			// Recognize tokens:
			int mark = currPos;
			for(Recognizer recognizer : recognizers) {
				try {
					Token token = recognizer.recognize(r, line, lineNum, currPos);
					if(token != null) {
						tokens.add(token);
						currPos += token.getLength();
						break;
					}
				}
				catch(RecognizeException re) {
					r.addError(re.getMessage(), new Position(lineNum+1, re.errorPosition));
					currPos = re.resumePosition;
					// Check whether we're not out of bounds:
					if(currPos >= line.length()) {
						continue run_recognizers;
					}
				}
			}
			// No token identified:
			if(mark == currPos) {
				r.addError("illegal token: '"+line.charAt(currPos)+"'", new Position(lineNum+1, currPos));
				currPos++;
			}
		}
	}
	
}
